feat(support): Uri fluent parser with immutable Uriable builder#121
Conversation
Implements Uri.of(url) → Uriable fluent API modelled on the existing Str/Stringable pattern. Uriable wraps urllib.parse.ParseResult and exposes read accessors (scheme, host, port, path, query, fragment) alongside immutable mutators (with_scheme, with_host, with_port, with_path, append_path, with_query, add_query, remove_query, with_fragment, without_fragment, without_query). Every mutator returns a new Uriable so the original is never modified. Exports Uri and Uriable from fastapi_startkit.support. 48 new tests added in tests/utils/test_uri.py covering accessors, each mutator, fluent chaining, immutability, equality, and __repr__. Closes #178 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
✅ Code Review: APPROVED (with notes)48/48 URI tests pass. Full suite: 1450 passed, 7 skipped — zero regressions. Checklist walkthrough1. Uri/Uriable structure and immutability ✓ Pattern correctly mirrors
Auth preservation across mutations also verified manually: 2. support/init.py exports ✓ Both 3. serve_command.py — urllib.parse → Uri.of() migration ℹ️ (N/A for this PR) On this branch 4. Tests in tests/utils/test_uri.py ✓ All 48 tests present. Fine location. 5. query().all(), .has(), .missing(), .decode() — 6. No regressions ✓ (inferring the truncated checklist item) Correctness spot-checks
Notes (non-blocking)Note 1 — PM checklist item 5 specified The SE's approach is simpler and idiomatic Python — callers can use standard dict operations ( Note 2 — Schemaless URLs require a scheme
Note 3 — Functional, but a named sentinel ( Verdict: APPROVED — safe to merge. |
Remove unused `pytest` import and reformat chained method call in tests/utils/test_uri.py to satisfy ruff lint (F401) and ruff format checks. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Summary
Uri.of(url)→Uriablefluent API modelled on the existingStr/Stringablepattern infastapi_startkit.supportUriablewrapsurllib.parse.ParseResultand exposes:scheme(),host(),port(),path(),query(),query_param(),fragment()with_scheme(),with_host(),with_port(),with_path(),append_path(),with_query(),add_query(),remove_query(),with_fragment(),without_fragment(),without_query()get()/__str__()to retrieve the final URL stringUriable(value-object semantics — original is never modified)UriandUriableare exported fromfastapi_startkit.supportuser:pass@) and port are preserved across mutationsTest plan
tests/utils/test_uri.pycovering: factory methods, all accessors, each mutator, full fluent chain, immutability across the chain, equality (==to string and anotherUriable), and__repr__Closes #178
🤖 Generated with Claude Code